#!/usr/bin/env python3
"""
网络调度器 - 第1.5阶段核心组件
负责检测网络环境，制定最优爬取计划，智能调度爬取任务
"""

import logging
import time
import yaml
import requests
import ipaddress
from typing import Dict, List, Optional, Any
from pathlib import Path
from datetime import datetime, timedelta

logger = logging.getLogger(__name__)

class NetworkScheduler:
    """网络环境智能调度器 - 第1.5阶段"""
    
    def __init__(self):
        """初始化网络调度器"""
        logger.info("[SCHEDULER] 初始化网络调度器...")
        
        try:
            # 加载配置
            self.config = self.load_scheduler_config()
            self.network_config = self.config.get('network_config', {})
            self.crawl_strategy = self.config.get('crawl_strategy', {})
            
            # 检测当前网络环境
            self.current_network = self.detect_network_environment()
            
            # 计算每日目标
            self.daily_targets = self.calculate_daily_targets()
            
            # 初始化统计数据
            self.session_stats = {
                'today_crawled': 0,
                'current_session_start': None,
                'sessions_completed': 0,
                'last_update': time.time()
            }
            
            logger.info(f"[SCHEDULER] ✅ 初始化完成")
            logger.info(f"[SCHEDULER] 当前网络环境: {self.current_network}")
            logger.info(f"[SCHEDULER] 每日目标: {sum(self.daily_targets.values())} 条")
            
        except Exception as e:
            logger.error(f"[SCHEDULER] ❌ 初始化失败: {e}")
            raise
    
    def load_scheduler_config(self) -> Dict:
        """加载调度器配置"""
        config_file = Path(__file__).parent.parent / "config" / "accounts.yaml"
        
        try:
            if config_file.exists():
                with open(config_file, 'r', encoding='utf-8') as f:
                    return yaml.safe_load(f)
            else:
                logger.warning(f"[SCHEDULER] 配置文件不存在: {config_file}")
                return self.get_default_config()
                
        except Exception as e:
            logger.error(f"[SCHEDULER] 配置加载失败: {e}")
            return self.get_default_config()
    
    def get_default_config(self) -> Dict:
        """获取默认配置"""
        return {
            'network_config': {
                'detection_method': 'time_based',
                'company_ip_ranges': ['192.168.1.0/24', '10.0.0.0/8'],
                'schedule': {
                    'company': {
                        'morning': '08:00-12:00',
                        'afternoon': '14:00-18:00'
                    },
                    'home': {
                        'evening': '19:00-23:00'
                    }
                }
            },
            'crawl_strategy': {
                'daily_targets': {
                    'morning_session': 700,
                    'afternoon_session': 700,
                    'evening_session': 600
                },
                'rotation_strategy': {
                    'max_data_per_account': 350,
                    'switch_threshold': 0.8,
                    'error_switch_threshold': 3
                }
            }
        }
    
    def detect_network_environment(self) -> str:
        """检测当前网络环境"""
        detection_method = self.network_config.get('detection_method', 'time_based')
        
        if detection_method == 'ip_based':
            return self.detect_by_ip()
        else:
            return self.detect_by_time()
    
    def detect_by_ip(self) -> str:
        """基于IP地址检测网络环境"""
        try:
            # 获取当前公网IP
            response = requests.get('https://httpbin.org/ip', timeout=5)
            current_ip = response.json()['origin'].split(',')[0].strip()
            
            logger.debug(f"[SCHEDULER] 当前IP: {current_ip}")
            
            # 检查是否在公司IP段内
            company_ranges = self.network_config.get('company_ip_ranges', [])
            
            for ip_range in company_ranges:
                try:
                    if ipaddress.ip_address(current_ip) in ipaddress.ip_network(ip_range, strict=False):
                        logger.info(f"[SCHEDULER] IP检测: 公司网络 ({current_ip})")
                        return 'company'
                except Exception as e:
                    logger.warning(f"[SCHEDULER] IP段检查失败 {ip_range}: {e}")
                    continue
            
            logger.info(f"[SCHEDULER] IP检测: 家庭网络 ({current_ip})")
            return 'home'
            
        except Exception as e:
            logger.warning(f"[SCHEDULER] IP检测失败，回退到时间检测: {e}")
            return self.detect_by_time()
    
    def detect_by_time(self) -> str:
        """基于时间判断网络环境"""
        current_hour = datetime.now().hour
        
        # 工作时间判断为公司网络 (8-18点)
        if 8 <= current_hour <= 18:
            logger.debug(f"[SCHEDULER] 时间检测: 公司网络 ({current_hour}点)")
            return 'company'
        else:
            logger.debug(f"[SCHEDULER] 时间检测: 家庭网络 ({current_hour}点)")
            return 'home'
    
    def calculate_daily_targets(self) -> Dict[str, int]:
        """计算每日目标分配"""
        strategy = self.crawl_strategy.get('daily_targets', {})
        
        return {
            'morning': strategy.get('morning_session', 700),
            'afternoon': strategy.get('afternoon_session', 700),
            'evening': strategy.get('evening_session', 600)
        }
    
    def get_current_time_slot(self) -> str:
        """获取当前时段"""
        current_hour = datetime.now().hour
        
        if 8 <= current_hour < 12:
            return 'morning'
        elif 14 <= current_hour < 18:
            return 'afternoon'
        elif 19 <= current_hour < 23:
            return 'evening'
        elif 0 <= current_hour < 2:
            return 'night'
        else:
            return 'off_peak'
    
    def get_optimal_crawl_plan(self) -> Dict:
        """获取最优爬取计划"""
        current_time = datetime.now()
        current_hour = current_time.hour
        network_type = self.current_network
        time_slot = self.get_current_time_slot()
        
        # 基于时段和网络环境制定计划
        if network_type == 'company' and time_slot in ['morning', 'afternoon']:
            target_data = self.daily_targets.get(time_slot, 700)
            
            plan = {
                'network_type': 'company',
                'time_slot': time_slot,
                'target_data': target_data,
                'max_accounts': 2,
                'session_duration': 240,  # 4小时
                'data_per_account': target_data // 2,
                'optimal': True,
                'priority': 'high'
            }
            
        elif network_type == 'home' and time_slot == 'evening':
            target_data = self.daily_targets.get('evening', 600)
            
            plan = {
                'network_type': 'home',
                'time_slot': 'evening',
                'target_data': target_data,
                'max_accounts': 2,
                'session_duration': 240,  # 4小时
                'data_per_account': target_data // 2,
                'optimal': True,
                'priority': 'high'
            }
            
        else:
            # 非最优时段
            plan = {
                'network_type': network_type,
                'time_slot': time_slot,
                'target_data': 200,
                'max_accounts': 1,
                'session_duration': 60,  # 1小时
                'data_per_account': 200,
                'optimal': False,
                'priority': 'low'
            }
        
        # 添加策略配置
        rotation_strategy = self.crawl_strategy.get('rotation_strategy', {})
        plan.update({
            'max_data_per_account': rotation_strategy.get('max_data_per_account', 350),
            'switch_threshold': rotation_strategy.get('switch_threshold', 0.8),
            'error_switch_threshold': rotation_strategy.get('error_switch_threshold', 3)
        })
        
        return plan
    
    def should_start_crawling(self) -> bool:
        """判断是否应该开始爬取"""
        plan = self.get_optimal_crawl_plan()
        
        # 非最优时段不建议爬取
        if not plan['optimal']:
            logger.info(f"[SCHEDULER] 当前时段 ({plan['time_slot']}) 不是最优爬取时间")
            return False
        
        # 检查今日目标是否已完成
        total_target = sum(self.daily_targets.values())
        if self.session_stats['today_crawled'] >= total_target:
            logger.info(f"[SCHEDULER] 今日目标已完成: {self.session_stats['today_crawled']}/{total_target}")
            return False
        
        # 检查当前时段目标是否已完成
        time_slot = plan['time_slot']
        slot_target = self.daily_targets.get(time_slot, 0)
        slot_completed = self.get_time_slot_completed(time_slot)
        
        if slot_completed >= slot_target:
            logger.info(f"[SCHEDULER] {time_slot} 时段目标已完成: {slot_completed}/{slot_target}")
            return False
        
        return True
    
    def get_session_config(self) -> Dict:
        """获取当前会话配置"""
        plan = self.get_optimal_crawl_plan()
        
        return {
            'network_type': plan['network_type'],
            'time_slot': plan['time_slot'],
            'max_data_per_session': plan['data_per_account'],
            'session_timeout': plan['session_duration'] * 60,  # 转换为秒
            'account_switch_interval': plan['session_duration'] // 2 * 60,
            'error_retry_limit': plan['error_switch_threshold'],
            'success_rate_threshold': 0.8,
            'target_data': plan['target_data'],
            'priority': plan['priority']
        }
    
    def update_session_progress(self, data_count: int):
        """更新会话进度"""
        self.session_stats['today_crawled'] += data_count
        self.session_stats['last_update'] = time.time()
        
        logger.debug(f"[SCHEDULER] 更新进度: +{data_count}, 今日总计: {self.session_stats['today_crawled']}")
    
    def start_session(self):
        """开始新会话"""
        self.session_stats['current_session_start'] = time.time()
        self.session_stats['sessions_completed'] += 1
        
        logger.info(f"[SCHEDULER] 开始第 {self.session_stats['sessions_completed']} 个会话")
    
    def get_time_slot_completed(self, time_slot: str) -> int:
        """获取指定时段已完成数量（简化实现）"""
        # 这里简化处理，实际应该根据时段统计
        # 可以后续扩展为更精确的时段统计
        return 0
    
    def get_daily_progress(self) -> Dict:
        """获取每日进度"""
        total_target = sum(self.daily_targets.values())
        completed = self.session_stats['today_crawled']
        
        return {
            'total_target': total_target,
            'completed': completed,
            'remaining': max(0, total_target - completed),
            'progress_rate': completed / total_target if total_target > 0 else 0,
            'targets_by_slot': self.daily_targets,
            'current_slot': self.get_current_time_slot()
        }
    
    def get_next_optimal_time(self) -> Optional[datetime]:
        """获取下一个最优爬取时间"""
        current_time = datetime.now()
        current_hour = current_time.hour
        
        # 定义最优时段
        optimal_slots = [
            (8, 12, 'company'),   # 早上
            (14, 18, 'company'),  # 下午
            (19, 23, 'home')      # 晚上
        ]
        
        for start_hour, end_hour, network in optimal_slots:
            if current_hour < start_hour:
                # 今天的时段还没到
                next_time = current_time.replace(hour=start_hour, minute=0, second=0, microsecond=0)
                return next_time
            elif start_hour <= current_hour < end_hour:
                # 当前就在最优时段
                return current_time
        
        # 今天的最优时段都过了，返回明天早上8点
        tomorrow = current_time + timedelta(days=1)
        next_time = tomorrow.replace(hour=8, minute=0, second=0, microsecond=0)
        return next_time
    
    def get_scheduler_status(self) -> Dict:
        """获取调度器状态"""
        plan = self.get_optimal_crawl_plan()
        progress = self.get_daily_progress()
        next_optimal = self.get_next_optimal_time()
        
        return {
            'current_network': self.current_network,
            'current_time_slot': self.get_current_time_slot(),
            'should_crawl': self.should_start_crawling(),
            'current_plan': plan,
            'daily_progress': progress,
            'next_optimal_time': next_optimal.strftime('%H:%M') if next_optimal else None,
            'session_stats': self.session_stats,
            'detection_method': self.network_config.get('detection_method', 'time_based')
        }
    
    def reset_daily_stats(self):
        """重置每日统计（每日0点调用）"""
        logger.info("[SCHEDULER] 重置每日统计数据")
        
        self.session_stats.update({
            'today_crawled': 0,
            'current_session_start': None,
            'sessions_completed': 0,
            'last_update': time.time()
        })
    
    def refresh_network_detection(self):
        """刷新网络环境检测"""
        old_network = self.current_network
        self.current_network = self.detect_network_environment()
        
        if old_network != self.current_network:
            logger.info(f"[SCHEDULER] 网络环境变化: {old_network} → {self.current_network}")
        
        return self.current_network
